package com.ruoyi.web.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.web.common.BusinessException;
import com.ruoyi.web.common.ProjectConstant;
import com.ruoyi.web.dto.AccountDetailResp;
import com.ruoyi.web.dto.BuyAssetReq;
import com.ruoyi.web.entity.AccountInfo;
import com.ruoyi.web.entity.AssetPrice;
import com.ruoyi.web.entity.FundAsset;
import com.ruoyi.web.entity.TradeDetail;
import com.ruoyi.web.mapper.AssetPriceMapper;
import com.ruoyi.web.mapper.FundAssetMapper;
import com.ruoyi.web.mapper.TradeDetailMapper;
import com.ruoyi.web.util.BigDecimalCheckUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.MessageFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <br>
 *
 * @author chenwujie
 * @date 2021/2/11
 */
@Service
public class AssetService extends ServiceImpl<FundAssetMapper, FundAsset> {
    @Resource
    FundAssetMapper fundAssetMapper;
    @Resource
    AccountInfoService accountInfoService;
    @Resource
    AssetPriceMapper assetPriceMapper;
    @Resource
    TradeDetailMapper tradeDetailMapper;


    public void buyAsset(BuyAssetReq buyAssetReq) {
        LocalDate today = LocalDate.now();

        LambdaQueryWrapper<FundAsset> findFundAsset = new QueryWrapper<FundAsset>()
                .lambda()
                .eq(FundAsset::getAssetName, buyAssetReq.getAssetName())
                .eq(FundAsset::getEndDate, ProjectConstant.FOREVER_DATE);
        FundAsset fundAsset = fundAssetMapper.selectOne(findFundAsset);
        if (fundAsset == null) {
            throw new BusinessException("资产{0}不存在，请检查", buyAssetReq.getAssetName());
        }

        // 检查流动资金是否充足
        AccountInfo currentAccount = accountInfoService.getCurrentAccount();
        BigDecimal amount = new BigDecimal(buyAssetReq.getAmount());
        if (currentAccount.getLiquidity().compareTo(amount) < 0) {
            throw new BusinessException("流动资金为{0}不足以支付，请检查", currentAccount.getLiquidity().toPlainString());
        }

        BigDecimal buyNumberOf = new BigDecimal(buyAssetReq.getNumberOf());

        // 更新资产价格信息
        BigDecimal price = null;
        if (!BigDecimalCheckUtils.isZero(buyNumberOf)) {
            price = amount
                    .divide(buyNumberOf, 4, RoundingMode.HALF_UP);
            updateAssetPrice(today, fundAsset.getTypeName(), fundAsset.getAssetName(), price);
        }


        // 更新资产
        if (today.equals(fundAsset.getStartDate())) {
            fundAsset.setNumberOf(fundAsset.getNumberOf().add(buyNumberOf));
            fundAsset.setCostOf(fundAsset.getCostOf().add(amount));
            fundAsset.setCostPrice(fundAsset.getCostOf().divide(fundAsset.getNumberOf(), 4, RoundingMode.HALF_UP));
            fundAssetMapper.updateById(fundAsset);
        } else {
            fundAsset.setEndDate(today);
            fundAssetMapper.updateById(fundAsset);

            FundAsset newFundAsset = new FundAsset();
            newFundAsset.setTypeName(fundAsset.getTypeName());
            newFundAsset.setAssetName(fundAsset.getAssetName());
            newFundAsset.setNumberOf(fundAsset.getNumberOf().add(buyNumberOf));
            newFundAsset.setCostOf(fundAsset.getCostOf().add(amount));
            newFundAsset.setCostPrice(newFundAsset.getCostOf().divide(newFundAsset.getNumberOf(), 4, RoundingMode.HALF_UP));
            newFundAsset.setHistoryEarnings(fundAsset.getHistoryEarnings());
            newFundAsset.setStartDate(today);
            newFundAsset.setEndDate(ProjectConstant.FOREVER_DATE);
            fundAssetMapper.insert(newFundAsset);
        }

        // 扣减流动资金，更新当天资产汇总
        accountInfoService.updateAccountSubtract(amount);

        // 记录交易信息
        TradeDetail entity = new TradeDetail();
        entity.setTypeName(fundAsset.getTypeName());
        entity.setAssetName(fundAsset.getAssetName());
        entity.setDirection("买入");
        entity.setNumberOf(buyNumberOf);
        entity.setAmount(amount);
        entity.setTradeTime(LocalDateTime.now());
        entity.setRemark(buyAssetReq.getRemark());
        tradeDetailMapper.insert(entity);
    }


    @Transactional(rollbackFor = Throwable.class)
    public void buyNewAsset(BuyAssetReq buyAssetReq) {
        BigDecimal amount = new BigDecimal(buyAssetReq.getAmount());
        BigDecimal buyNumberOf = new BigDecimal(buyAssetReq.getNumberOf());
        LocalDate today = LocalDate.now();


        // 检查流动资金是否充足
        AccountInfo currentAccount = accountInfoService.getCurrentAccount();
        if (currentAccount.getLiquidity().compareTo(amount) < 0) {
            throw new BusinessException("流动资金为{0}不足以支付，请检查", currentAccount.getLiquidity().toPlainString());
        }

        // 检查是否是新的资产
        LambdaQueryWrapper<FundAsset> checkFundAsset = new QueryWrapper<FundAsset>()
                .lambda()
                .eq(FundAsset::getAssetName, buyAssetReq.getNewAssetName());
        if (fundAssetMapper.selectCount(checkFundAsset) > 0) {
            throw new BusinessException("资产{0}存在，请从下拉框中选择", buyAssetReq.getNewAssetName());
        }


        // 更新资产价格信息
        BigDecimal price = amount.divide(buyNumberOf, 4, RoundingMode.HALF_UP);
        updateAssetPrice(today, buyAssetReq.getTypeName(), buyAssetReq.getNewAssetName(), price);

        // 新增资产信息
        FundAsset fundAsset = new FundAsset();
        fundAsset.setTypeName(buyAssetReq.getTypeName());
        fundAsset.setAssetName(buyAssetReq.getNewAssetName());
        fundAsset.setNumberOf(buyNumberOf);
        fundAsset.setCostOf(amount);
        fundAsset.setCostPrice(price);
        fundAsset.setHistoryEarnings(BigDecimal.ZERO);
        fundAsset.setStartDate(today);
        fundAsset.setEndDate(ProjectConstant.FOREVER_DATE);
        fundAssetMapper.insert(fundAsset);


        // 扣减流动资金，更新当天资产汇总
        accountInfoService.updateAccountSubtract(amount);

        // 记录交易信息
        TradeDetail entity = new TradeDetail();
        entity.setTypeName(fundAsset.getTypeName());
        entity.setAssetName(fundAsset.getAssetName());
        entity.setDirection("买入");
        entity.setNumberOf(buyNumberOf);
        entity.setAmount(amount);
        entity.setTradeTime(LocalDateTime.now());
        entity.setRemark(buyAssetReq.getRemark());
        tradeDetailMapper.insert(entity);
    }


    public List<FundAsset> getAllAsset() {
        Wrapper<FundAsset> queryWrapper = new QueryWrapper<FundAsset>()
                .lambda()
                .eq(FundAsset::getEndDate, ProjectConstant.FOREVER_DATE);
        return fundAssetMapper.selectList(queryWrapper);
    }

    @Transactional
    public void updateAssetPrice(LocalDate localDate, String typeName, String assetName, BigDecimal price) {
        // 更新总账
        accountInfoService.updateAccountAdd(BigDecimal.ZERO);

        // 更新资产价格信息
        Wrapper<AssetPrice> queryWrapper = new QueryWrapper<AssetPrice>()
                .lambda()
                .eq(AssetPrice::getTypeName, typeName)
                .eq(AssetPrice::getAssetName, assetName)
                .le(AssetPrice::getStartDate, localDate)
                .gt(AssetPrice::getEndDate, localDate);


        AssetPrice assetPrice = assetPriceMapper.selectOne(queryWrapper);


        if (assetPrice == null) {
            // 新增资产价格信息
            AssetPrice newAssetPrice = new AssetPrice();
            newAssetPrice.setTypeName(typeName);
            newAssetPrice.setAssetName(assetName);
            newAssetPrice.setPrice(price);
            newAssetPrice.setStartDate(localDate);
            newAssetPrice.setEndDate(ProjectConstant.FOREVER_DATE);
            assetPriceMapper.insert(newAssetPrice);
        } else {
            if (localDate.equals(assetPrice.getStartDate())) {
                assetPrice.setPrice(price);
                assetPriceMapper.updateById(assetPrice);
            } else {
                assetPrice.setEndDate(localDate);
                assetPriceMapper.updateById(assetPrice);

                // 新增资产价格信息
                AssetPrice newAssetPrice = new AssetPrice();
                newAssetPrice.setTypeName(typeName);
                newAssetPrice.setAssetName(assetName);
                newAssetPrice.setPrice(price);
                newAssetPrice.setStartDate(localDate);
                newAssetPrice.setEndDate(ProjectConstant.FOREVER_DATE);
                assetPriceMapper.insert(newAssetPrice);
            }
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    public void saleAsset(BuyAssetReq buyAssetReq) {
//        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
//        TransactionStatus status = transactionManager.getTransaction(def);

        boolean inTransaction = TransactionSynchronizationManager.isActualTransactionActive();

        if (inTransaction) {
            // 事务已经存在
        } else {
            // 事务不存在
        }

        TransactionStatus transactionStatus = TransactionAspectSupport.currentTransactionStatus();

        LocalDate today = LocalDate.now();

        LambdaQueryWrapper<FundAsset> findFundAsset = new QueryWrapper<FundAsset>()
                .lambda()
                .eq(FundAsset::getAssetName, buyAssetReq.getAssetName())
                .eq(FundAsset::getEndDate, ProjectConstant.FOREVER_DATE);
        FundAsset fundAsset = fundAssetMapper.selectOne(findFundAsset);
        if (fundAsset == null) {
            throw new BusinessException("资产{0}不存在，请检查", buyAssetReq.getAssetName());
        }


        String typeName = fundAsset.getTypeName();
        String assetName = fundAsset.getAssetName();

        // 更新资产价格
        BigDecimal numberOf = new BigDecimal(buyAssetReq.getNumberOf());
        BigDecimal price = null;
        if (!BigDecimalCheckUtils.isZero(numberOf)) {
            price = new BigDecimal(buyAssetReq.getAmount())
                    .divide(numberOf, 4, RoundingMode.HALF_UP);
            updateAssetPrice(today, typeName, assetName, price);
        }


        /* 更新资产*/
        //本次卖出份额
        BigDecimal saleNumberOf = numberOf;
        //剩余份额
        BigDecimal remainNumberOf = fundAsset.getNumberOf().subtract(saleNumberOf);
        //卖出成本
        BigDecimal saleCostOf = saleNumberOf.multiply(fundAsset.getCostPrice());
        //新的成本 = 原成本-卖出成本
        BigDecimal newCostOf = fundAsset.getCostOf()
                .subtract(saleCostOf)
                .setScale(2, RoundingMode.HALF_UP);
        //卖出收益
        BigDecimal saleAmount = new BigDecimal(buyAssetReq.getAmount());
        //本次收益 = 卖出收益 - 卖出成本
        BigDecimal currentEarnings = saleAmount.subtract(saleCostOf);
        //新的历史收益 = 原历史收益 + 本次收益
        BigDecimal newHistoryEarnings = fundAsset.getHistoryEarnings().add(currentEarnings);
        //新的成本   （这里基本不变，为了精确再算一次）
        BigDecimal newCostPrice = BigDecimalCheckUtils.isZero(remainNumberOf) ?
                BigDecimal.ZERO :
                newCostOf.divide(remainNumberOf, 4, RoundingMode.HALF_UP);


        if (remainNumberOf.compareTo(BigDecimal.ZERO) < 0) {
            throw new BusinessException("资产份额卖出后为负数：{0}", remainNumberOf.toPlainString());
        }

        if (today.equals(fundAsset.getStartDate())) {
            fundAsset.setNumberOf(remainNumberOf);
            fundAsset.setCostOf(newCostOf);
            fundAsset.setCostPrice(newCostPrice);
            fundAsset.setHistoryEarnings(newHistoryEarnings);
            fundAssetMapper.updateById(fundAsset);
        } else {
            fundAsset.setEndDate(today);
            fundAssetMapper.updateById(fundAsset);

            FundAsset newFundAsset = new FundAsset();
            newFundAsset.setTypeName(typeName);
            newFundAsset.setAssetName(assetName);
            newFundAsset.setNumberOf(remainNumberOf);
            newFundAsset.setCostOf(newCostOf);
            newFundAsset.setCostPrice(newCostPrice);
            newFundAsset.setHistoryEarnings(newHistoryEarnings);
            newFundAsset.setStartDate(today);
            newFundAsset.setEndDate(ProjectConstant.FOREVER_DATE);
            fundAssetMapper.insert(newFundAsset);
        }

        // 更新流动资金
        accountInfoService.updateAccountAdd(saleAmount);

        // 记录交易信息
        TradeDetail entity = new TradeDetail();
        entity.setTypeName(fundAsset.getTypeName());
        entity.setAssetName(fundAsset.getAssetName());
        entity.setDirection("卖出");
        entity.setNumberOf(saleNumberOf);
        entity.setAmount(saleAmount);
        entity.setTradeTime(LocalDateTime.now());
        entity.setRemark(buyAssetReq.getRemark());
        tradeDetailMapper.insert(entity);
    }

    public AssetPrice fetchPrice(FundAsset fundAsset) {
        LambdaQueryWrapper<AssetPrice> param = new QueryWrapper<AssetPrice>()
                .lambda()
                .eq(AssetPrice::getTypeName, fundAsset.getTypeName())
                .eq(AssetPrice::getAssetName, fundAsset.getAssetName())
                .le(AssetPrice::getStartDate, fundAsset.getEndDate())
                .ge(AssetPrice::getEndDate, fundAsset.getEndDate())
                .orderByDesc(AssetPrice::getEndDate)
                .last("limit 0,1");
        AssetPrice assetPrice = assetPriceMapper.selectOne(param);
        return assetPrice;
    }

    public List<AccountDetailResp.AssetDetailResp> calcAssetDetail(LocalDate date, String typeName) {
        // 查询所有持有不是0的基金
        LambdaQueryWrapper<FundAsset> query = new QueryWrapper<FundAsset>()
                .lambda()
                .ne(FundAsset::getNumberOf, BigDecimal.ZERO)
                .eq(FundAsset::getTypeName, typeName)
                .le(FundAsset::getStartDate, date)
                .gt(FundAsset::getEndDate, date);
        List<FundAsset> fundAssets = fundAssetMapper.selectList(query);

        return fundAssets.stream()
                .sorted(Comparator.comparing(FundAsset::getAssetName))
                .map(fundAsset -> {
                    AccountDetailResp.AssetDetailResp assetDetailResp = new AccountDetailResp.AssetDetailResp();
                    /* 查询一下最新的价格*/
                    LambdaQueryWrapper<AssetPrice> param = new QueryWrapper<AssetPrice>()
                            .lambda()
                            .eq(AssetPrice::getTypeName, fundAsset.getTypeName())
                            .eq(AssetPrice::getAssetName, fundAsset.getAssetName())
                            .le(AssetPrice::getStartDate, date)
                            .gt(AssetPrice::getEndDate, date);
                    AssetPrice assetPrice = assetPriceMapper.selectOne(param);
                    if (assetPrice == null) {
                        throw new RuntimeException(MessageFormat.format("名称：{0}，日期：{1}未能找到价格信息", fundAsset.getAssetName(), date));
                    }

                    BigDecimal markValue = assetPrice.getPrice().multiply(fundAsset.getNumberOf());  // 市值 = 股票当前价 * 持有数量
                    BigDecimal costOf = fundAsset.getCostOf();   // 股票成本总价

                    assetDetailResp.setTypeName(fundAsset.getTypeName());
                    assetDetailResp.setAssetName(fundAsset.getAssetName());
                    assetDetailResp.setCostOf(costOf.toPlainString());
                    assetDetailResp.setCostPrice(fundAsset.getCostPrice().toPlainString());
                    assetDetailResp.setNumberOf(fundAsset.getNumberOf().toPlainString());
                    assetDetailResp.setPrice(assetPrice.getPrice().toPlainString());
                    assetDetailResp.setMarketValue(markValue.setScale(2, RoundingMode.HALF_UP).toPlainString());
                    /** 当前收益  =   当前市价 - 成本价    */
                    assetDetailResp.setCurrentEarnings(markValue.subtract(costOf).setScale(2, RoundingMode.HALF_UP).toPlainString());
                    assetDetailResp.setHistoryEarnings(fundAsset.getHistoryEarnings().toPlainString());

                    return assetDetailResp;
                })
                .collect(Collectors.toList());
    }

}